using Microsoft.Expression.Drawing.Core;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace Microsoft.Expression.Media
{
	internal class ArcGeometrySource : GeometrySource<IArcGeometrySourceParameters>
	{
		private double relativeThickness;
		private double absoluteThickness;
		protected override Rect ComputeLogicalBounds(Rect layoutBounds, IGeometrySourceParameters parameters)
		{
			Rect logicalBound = base.ComputeLogicalBounds(layoutBounds, parameters);
			return GeometryHelper.GetStretchBound(logicalBound, parameters.Stretch, new Size(1.0, 1.0));
		}
		protected override bool UpdateCachedGeometry(IArcGeometrySourceParameters parameters)
		{
			bool flag = false;
			this.NormalizeThickness(parameters);
			bool relativeMode = parameters.ArcThicknessUnit == UnitType.Percent;
			bool flag2 = MathHelper.AreClose(parameters.StartAngle, parameters.EndAngle);
			double num = ArcGeometrySource.NormalizeAngle(parameters.StartAngle);
			double num2 = ArcGeometrySource.NormalizeAngle(parameters.EndAngle);
			if (num2 < num)
			{
				num2 += 360.0;
			}
			bool flag3 = this.relativeThickness == 1.0;
			bool flag4 = this.relativeThickness == 0.0;
			if (flag2)
			{
				flag |= this.UpdateZeroAngleGeometry(relativeMode, num);
			}
			else
			{
				if (MathHelper.IsVerySmall((num2 - num) % 360.0))
				{
					if (!flag4 && !flag3)
					{
						flag |= this.UpdateFullRingGeometry(relativeMode);
					}
					else
					{
						flag |= this.UpdateEllipseGeometry(flag3);
					}
				}
				else
				{
					if (flag3)
					{
						flag |= this.UpdatePieGeometry(num, num2);
					}
					else
					{
						if (flag4)
						{
							flag |= this.UpdateOpenArcGeometry(num, num2);
						}
						else
						{
							flag |= this.UpdateRingArcGeometry(relativeMode, num, num2);
						}
					}
				}
			}
			return flag;
		}
		private void NormalizeThickness(IArcGeometrySourceParameters parameters)
		{
			double num = base.LogicalBounds.get_Width() / 2.0;
			double num2 = base.LogicalBounds.get_Height() / 2.0;
			double num3 = Math.Min(num, num2);
			double num4 = parameters.ArcThickness;
			if (parameters.ArcThicknessUnit == UnitType.Pixel)
			{
				num4 = MathHelper.SafeDivide(num4, num3, 0.0);
			}
			this.relativeThickness = MathHelper.EnsureRange(num4, new double?(0.0), new double?(1.0));
			this.absoluteThickness = num3 * this.relativeThickness;
		}
		private bool UpdateZeroAngleGeometry(bool relativeMode, double angle)
		{
			bool flag = false;
			Point arcPoint = GeometryHelper.GetArcPoint(angle, base.LogicalBounds);
			Rect logicalBounds = base.LogicalBounds;
			double num = logicalBounds.get_Width() / 2.0;
			double num2 = logicalBounds.get_Height() / 2.0;
			Point point;
			if (!relativeMode && !MathHelper.AreClose(num, num2))
			{
				double intersect = ArcGeometrySource.InnerCurveSelfIntersect(num, num2, this.absoluteThickness);
				double[] array = ArcGeometrySource.ComputeAngleRanges(num, num2, intersect, angle, angle);
				double num3 = array[0] * 3.1415926535897931 / 180.0;
				Vector vector = new Vector(num2 * Math.Sin(num3), -num * Math.Cos(num3));
				point = GeometryHelper.GetArcPoint(array[0], base.LogicalBounds) - vector.Normalized() * this.absoluteThickness;
			}
			else
			{
				Rect bound = base.LogicalBounds.Resize(1.0 - this.relativeThickness);
				point = GeometryHelper.GetArcPoint(angle, bound);
			}
			LineGeometry dependencyObject;
			return flag | GeometryHelper.EnsureGeometryType<LineGeometry>(out dependencyObject, ref this.cachedGeometry, () => new LineGeometry()) | dependencyObject.SetIfDifferent(LineGeometry.StartPointProperty, arcPoint) | dependencyObject.SetIfDifferent(LineGeometry.EndPointProperty, point);
		}
		private bool UpdateEllipseGeometry(bool isFilled)
		{
			double num = MathHelper.Lerp(base.LogicalBounds.get_Top(), base.LogicalBounds.get_Bottom(), 0.5);
			Point point = new Point(base.LogicalBounds.get_Left(), num);
			Point point2 = new Point(base.LogicalBounds.get_Right(), num);
			PathGeometry pathGeometry;
			bool flag = false | GeometryHelper.EnsureGeometryType<PathGeometry>(out pathGeometry, ref this.cachedGeometry, () => new PathGeometry()) | pathGeometry.get_Figures().EnsureListCount(1, () => new PathFigure());
			PathFigure pathFigure = pathGeometry.get_Figures().get_Item(0);
			ArcSegment dependencyObject;
			ArcSegment dependencyObject2;
			flag = (flag | pathFigure.SetIfDifferent(PathFigure.IsClosedProperty, true) | pathFigure.SetIfDifferent(PathFigure.IsFilledProperty, isFilled) | pathFigure.get_Segments().EnsureListCount(2, () => new ArcSegment()) | pathFigure.SetIfDifferent(PathFigure.StartPointProperty, point) | GeometryHelper.EnsureSegmentType<ArcSegment>(out dependencyObject, pathFigure.get_Segments(), 0, () => new ArcSegment()) | GeometryHelper.EnsureSegmentType<ArcSegment>(out dependencyObject2, pathFigure.get_Segments(), 1, () => new ArcSegment()));
			Size size = new Size(base.LogicalBounds.get_Width() / 2.0, base.LogicalBounds.get_Height() / 2.0);
			return flag | dependencyObject.SetIfDifferent(ArcSegment.IsLargeArcProperty, false) | dependencyObject.SetIfDifferent(ArcSegment.SizeProperty, size) | dependencyObject.SetIfDifferent(ArcSegment.SweepDirectionProperty, 1) | dependencyObject.SetIfDifferent(ArcSegment.PointProperty, point2) | dependencyObject2.SetIfDifferent(ArcSegment.IsLargeArcProperty, false) | dependencyObject2.SetIfDifferent(ArcSegment.SizeProperty, size) | dependencyObject2.SetIfDifferent(ArcSegment.SweepDirectionProperty, 1) | dependencyObject2.SetIfDifferent(ArcSegment.PointProperty, point);
		}
		private bool UpdateFullRingGeometry(bool relativeMode)
		{
			PathGeometry pathGeometry;
			bool flag = false | GeometryHelper.EnsureGeometryType<PathGeometry>(out pathGeometry, ref this.cachedGeometry, () => new PathGeometry()) | pathGeometry.SetIfDifferent(PathGeometry.FillRuleProperty, 0) | pathGeometry.get_Figures().EnsureListCount(2, () => new PathFigure()) | PathFigureHelper.SyncEllipseFigure(pathGeometry.get_Figures().get_Item(0), base.LogicalBounds, 1, true);
			Rect logicalBounds = base.LogicalBounds;
			double num = logicalBounds.get_Width() / 2.0;
			double num2 = logicalBounds.get_Height() / 2.0;
			if (!relativeMode && !MathHelper.AreClose(num, num2))
			{
				flag = (flag | pathGeometry.get_Figures().get_Item(1).SetIfDifferent(PathFigure.IsClosedProperty, true) | pathGeometry.get_Figures().get_Item(1).SetIfDifferent(PathFigure.IsFilledProperty, true));
				Point point = default(Point);
				double intersect = ArcGeometrySource.InnerCurveSelfIntersect(num, num2, this.absoluteThickness);
				double[] angles = ArcGeometrySource.ComputeAngleRanges(num, num2, intersect, 360.0, 0.0);
				flag = (flag | this.SyncPieceWiseInnerCurves(pathGeometry.get_Figures().get_Item(1), 0, ref point, angles) | pathGeometry.get_Figures().get_Item(1).SetIfDifferent(PathFigure.StartPointProperty, point));
			}
			else
			{
				Rect bounds = base.LogicalBounds.Resize(1.0 - this.relativeThickness);
				flag |= PathFigureHelper.SyncEllipseFigure(pathGeometry.get_Figures().get_Item(1), bounds, 0, true);
			}
			return flag;
		}
		private static void IncreaseDuplicatedIndex(IList<double> values, ref int index)
		{
			while (index < values.get_Count() - 1)
			{
				if (values.get_Item(index) != values.get_Item(index + 1))
				{
					break;
				}
				index++;
			}
		}
		private static void DecreaseDuplicatedIndex(IList<double> values, ref int index)
		{
			while (index > 0)
			{
				if (values.get_Item(index) != values.get_Item(index - 1))
				{
					break;
				}
				index--;
			}
		}
		internal static double[] ComputeAngleRanges(double radiusX, double radiusY, double intersect, double start, double end)
		{
			List<double> list = new List<double>();
			list.Add(start);
			list.Add(end);
			list.Add(intersect);
			list.Add(180.0 - intersect);
			list.Add(180.0 + intersect);
			list.Add(360.0 - intersect);
			list.Add(360.0 + intersect);
			list.Add(540.0 - intersect);
			list.Add(540.0 + intersect);
			list.Add(720.0 - intersect);
			List<double> list2 = list;
			list2.Sort();
			int num = list2.IndexOf(start);
			int num2 = list2.IndexOf(end);
			if (num2 == num)
			{
				num2++;
			}
			else
			{
				if (start < end)
				{
					ArcGeometrySource.IncreaseDuplicatedIndex(list2, ref num);
					ArcGeometrySource.DecreaseDuplicatedIndex(list2, ref num2);
				}
				else
				{
					if (start > end)
					{
						ArcGeometrySource.DecreaseDuplicatedIndex(list2, ref num);
						ArcGeometrySource.IncreaseDuplicatedIndex(list2, ref num2);
					}
				}
			}
			List<double> list3 = new List<double>();
			if (num < num2)
			{
				for (int i = num; i <= num2; i++)
				{
					list3.Add(list2.get_Item(i));
				}
			}
			else
			{
				for (int j = num; j >= num2; j--)
				{
					list3.Add(list2.get_Item(j));
				}
			}
			double num3 = ArcGeometrySource.EnsureFirstQuadrant((list3.get_Item(0) + list3.get_Item(1)) / 2.0);
			if ((radiusX < radiusY && num3 < intersect) || (radiusX > radiusY && num3 > intersect))
			{
				list3.RemoveAt(0);
			}
			if (list3.get_Count() % 2 == 1)
			{
				list3.RemoveLast<double>();
			}
			if (list3.get_Count() == 0)
			{
				int num4 = Math.Min(num, num2) - 1;
				if (num4 < 0)
				{
					num4 = Math.Max(num, num2) + 1;
				}
				list3.Add(list2.get_Item(num4));
				list3.Add(list2.get_Item(num4));
			}
			return list3.ToArray();
		}
		internal static double EnsureFirstQuadrant(double angle)
		{
			angle = Math.Abs(angle % 180.0);
			if (angle <= 90.0)
			{
				return angle;
			}
			return 180.0 - angle;
		}
		private bool UpdatePieGeometry(double start, double end)
		{
			bool flag = false;
			PathGeometry pathGeometry = this.cachedGeometry as PathGeometry;
			PathFigure pathFigure;
			ArcSegment arcSegment;
			LineSegment dependencyObject;
			if (pathGeometry == null || pathGeometry.get_Figures().get_Count() != 1 || (pathFigure = pathGeometry.get_Figures().get_Item(0)).get_Segments().get_Count() != 2 || (arcSegment = (pathFigure.get_Segments().get_Item(0) as ArcSegment)) == null || (dependencyObject = (pathFigure.get_Segments().get_Item(1) as LineSegment)) == null)
			{
				(this.cachedGeometry = new PathGeometry()).get_Figures().Add(pathFigure = new PathFigure());
				pathFigure.get_Segments().Add(arcSegment = new ArcSegment());
				pathFigure.get_Segments().Add(dependencyObject = new LineSegment());
				pathFigure.set_IsClosed(true);
				arcSegment.set_SweepDirection(1);
				flag = true;
			}
			return flag | pathFigure.SetIfDifferent(PathFigure.StartPointProperty, GeometryHelper.GetArcPoint(start, base.LogicalBounds)) | arcSegment.SetIfDifferent(ArcSegment.PointProperty, GeometryHelper.GetArcPoint(end, base.LogicalBounds)) | arcSegment.SetIfDifferent(ArcSegment.SizeProperty, ArcGeometrySource.GetArcSize(base.LogicalBounds)) | arcSegment.SetIfDifferent(ArcSegment.IsLargeArcProperty, end - start > 180.0) | dependencyObject.SetIfDifferent(LineSegment.PointProperty, base.LogicalBounds.Center());
		}
		private bool UpdateOpenArcGeometry(double start, double end)
		{
			bool flag = false;
			PathGeometry pathGeometry = this.cachedGeometry as PathGeometry;
			PathFigure pathFigure;
			ArcSegment arcSegment;
			if (pathGeometry == null || pathGeometry.get_Figures().get_Count() != 1 || (pathFigure = pathGeometry.get_Figures().get_Item(0)).get_Segments().get_Count() != 1 || (arcSegment = (pathFigure.get_Segments().get_Item(0) as ArcSegment)) == null)
			{
				(this.cachedGeometry = new PathGeometry()).get_Figures().Add(pathFigure = new PathFigure());
				pathFigure.get_Segments().Add(arcSegment = new ArcSegment());
				pathFigure.set_IsClosed(false);
				arcSegment.set_SweepDirection(1);
				flag = true;
			}
			return flag | pathFigure.SetIfDifferent(PathFigure.StartPointProperty, GeometryHelper.GetArcPoint(start, base.LogicalBounds)) | pathFigure.SetIfDifferent(PathFigure.IsFilledProperty, false) | arcSegment.SetIfDifferent(ArcSegment.PointProperty, GeometryHelper.GetArcPoint(end, base.LogicalBounds)) | arcSegment.SetIfDifferent(ArcSegment.SizeProperty, ArcGeometrySource.GetArcSize(base.LogicalBounds)) | arcSegment.SetIfDifferent(ArcSegment.IsLargeArcProperty, end - start > 180.0);
		}
		private bool UpdateRingArcGeometry(bool relativeMode, double start, double end)
		{
			PathGeometry pathGeometry;
			bool flag = false | GeometryHelper.EnsureGeometryType<PathGeometry>(out pathGeometry, ref this.cachedGeometry, () => new PathGeometry()) | pathGeometry.SetIfDifferent(PathGeometry.FillRuleProperty, 1) | pathGeometry.get_Figures().EnsureListCount(1, () => new PathFigure());
			PathFigure pathFigure = pathGeometry.get_Figures().get_Item(0);
			ArcSegment dependencyObject;
			LineSegment dependencyObject2;
			flag = (flag | pathFigure.SetIfDifferent(PathFigure.IsClosedProperty, true) | pathFigure.SetIfDifferent(PathFigure.IsFilledProperty, true) | pathFigure.SetIfDifferent(PathFigure.StartPointProperty, GeometryHelper.GetArcPoint(start, base.LogicalBounds)) | pathFigure.get_Segments().EnsureListCountAtLeast(3, () => new ArcSegment()) | GeometryHelper.EnsureSegmentType<ArcSegment>(out dependencyObject, pathFigure.get_Segments(), 0, () => new ArcSegment()) | dependencyObject.SetIfDifferent(ArcSegment.PointProperty, GeometryHelper.GetArcPoint(end, base.LogicalBounds)) | dependencyObject.SetIfDifferent(ArcSegment.SizeProperty, new Size(base.LogicalBounds.get_Width() / 2.0, base.LogicalBounds.get_Height() / 2.0)) | dependencyObject.SetIfDifferent(ArcSegment.IsLargeArcProperty, end - start > 180.0) | dependencyObject.SetIfDifferent(ArcSegment.SweepDirectionProperty, 1) | GeometryHelper.EnsureSegmentType<LineSegment>(out dependencyObject2, pathFigure.get_Segments(), 1, () => new LineSegment()));
			Rect logicalBounds = base.LogicalBounds;
			double num = logicalBounds.get_Width() / 2.0;
			double num2 = logicalBounds.get_Height() / 2.0;
			if (!relativeMode && !MathHelper.AreClose(num, num2))
			{
				Point point = default(Point);
				double intersect = ArcGeometrySource.InnerCurveSelfIntersect(num, num2, this.absoluteThickness);
				double[] angles = ArcGeometrySource.ComputeAngleRanges(num, num2, intersect, end, start);
				flag = (flag | this.SyncPieceWiseInnerCurves(pathFigure, 2, ref point, angles) | dependencyObject2.SetIfDifferent(LineSegment.PointProperty, point));
			}
			else
			{
				Rect bound = base.LogicalBounds.Resize(1.0 - this.relativeThickness);
				ArcSegment dependencyObject3;
				flag = (flag | dependencyObject2.SetIfDifferent(LineSegment.PointProperty, GeometryHelper.GetArcPoint(end, bound)) | pathFigure.get_Segments().EnsureListCount(3, () => new ArcSegment()) | GeometryHelper.EnsureSegmentType<ArcSegment>(out dependencyObject3, pathFigure.get_Segments(), 2, () => new ArcSegment()) | dependencyObject3.SetIfDifferent(ArcSegment.PointProperty, GeometryHelper.GetArcPoint(start, bound)) | dependencyObject3.SetIfDifferent(ArcSegment.SizeProperty, ArcGeometrySource.GetArcSize(bound)) | dependencyObject3.SetIfDifferent(ArcSegment.IsLargeArcProperty, end - start > 180.0) | dependencyObject3.SetIfDifferent(ArcSegment.SweepDirectionProperty, 0));
			}
			return flag;
		}
		private bool SyncPieceWiseInnerCurves(PathFigure figure, int index, ref Point firstPoint, params double[] angles)
		{
			int num = angles.Length;
			Rect logicalBounds = base.LogicalBounds;
			double offset = this.absoluteThickness;
			bool flag = false | figure.get_Segments().EnsureListCount(index + num / 2, () => new PolyBezierSegment());
			for (int i = 0; i < num / 2; i++)
			{
				IList<Point> list = ArcGeometrySource.ComputeOneInnerCurve(angles[i * 2], angles[i * 2 + 1], logicalBounds, offset);
				if (i == 0)
				{
					firstPoint = list.get_Item(0);
				}
				flag |= PathSegmentHelper.SyncPolyBezierSegment(figure.get_Segments(), index + i, list, 1, list.get_Count() - 1);
			}
			return flag;
		}
		private static IList<Point> ComputeOneInnerCurve(double start, double end, Rect bounds, double offset)
		{
			double num = bounds.get_Width() / 2.0;
			double num2 = bounds.get_Height() / 2.0;
			Point point = bounds.Center();
			start = start * 3.1415926535897931 / 180.0;
			end = end * 3.1415926535897931 / 180.0;
			double num3 = 0.17453292519943295;
			int num4 = (int)Math.Ceiling(Math.Abs(end - start) / num3);
			num4 = Math.Max(2, num4);
			List<Point> list = new List<Point>(num4);
			List<Vector> list2 = new List<Vector>(num4);
			Point point2 = default(Point);
			Point point3 = default(Point);
			Vector vector = default(Vector);
			Vector vector2 = default(Vector);
			Vector vector3 = default(Vector);
			Vector vector4 = default(Vector);
			for (int i = 0; i < num4; i++)
			{
				double num5 = MathHelper.Lerp(start, end, (double)i / (double)(num4 - 1));
				double num6 = Math.Sin(num5);
				double num7 = Math.Cos(num5);
				point2.set_X(point.get_X() + num * num6);
				point2.set_Y(point.get_Y() - num2 * num7);
				vector.X = num * num7;
				vector.Y = num2 * num6;
				vector2.X = -num2 * num6;
				vector2.Y = num * num7;
				double num8 = num2 * num2 * num6 * num6 + num * num * num7 * num7;
				double num9 = Math.Sqrt(num8);
				double num10 = 2.0 * num6 * num7 * (num2 * num2 - num * num);
				vector3.X = -num2 * num7;
				vector3.Y = -num * num6;
				point3.set_X(point2.get_X() + offset * vector2.X / num9);
				point3.set_Y(point2.get_Y() + offset * vector2.Y / num9);
				vector4.X = vector.X + offset / num9 * (vector3.X - 0.5 * vector2.X / num8 * num10);
				vector4.Y = vector.Y + offset / num9 * (vector3.Y - 0.5 * vector2.Y / num8 * num10);
				list.Add(point3);
				list2.Add(-vector4.Normalized());
			}
			List<Point> list3 = new List<Point>(num4 * 3 + 1);
			list3.Add(list.get_Item(0));
			for (int j = 1; j < num4; j++)
			{
				point2 = list.get_Item(j - 1);
				point3 = list.get_Item(j);
				double scalar = GeometryHelper.Distance(point2, point3) / 3.0;
				list3.Add(point2 + list2.get_Item(j - 1) * scalar);
				list3.Add(point3 - list2.get_Item(j) * scalar);
				list3.Add(point3);
			}
			return list3;
		}
		internal static double InnerCurveSelfIntersect(double radiusX, double radiusY, double thickness)
		{
			double num = 0.0;
			double num2 = 1.5707963267948966;
			bool flag = radiusX <= radiusY;
			Vector vector = default(Vector);
			while (!ArcGeometrySource.AreCloseEnough(num, num2))
			{
				double num3 = (num + num2) / 2.0;
				double num4 = Math.Cos(num3);
				double num5 = Math.Sin(num3);
				vector.X = radiusY * num5;
				vector.Y = radiusX * num4;
				vector.Normalize();
				if (flag)
				{
					double num6 = radiusX * num5 - vector.X * thickness;
					if (num6 > 0.0)
					{
						num2 = num3;
					}
					else
					{
						if (num6 < 0.0)
						{
							num = num3;
						}
					}
				}
				else
				{
					double num7 = radiusY * num4 - vector.Y * thickness;
					if (num7 < 0.0)
					{
						num2 = num3;
					}
					else
					{
						if (num7 > 0.0)
						{
							num = num3;
						}
					}
				}
			}
			double num8 = (num + num2) / 2.0;
			if (ArcGeometrySource.AreCloseEnough(num8, 0.0))
			{
				return 0.0;
			}
			if (!ArcGeometrySource.AreCloseEnough(num8, 1.5707963267948966))
			{
				return num8 * 180.0 / 3.1415926535897931;
			}
			return 90.0;
		}
		private static bool AreCloseEnough(double angleA, double angleB)
		{
			return Math.Abs(angleA - angleB) < 0.001;
		}
		private static Size GetArcSize(Rect bound)
		{
			return new Size(bound.get_Width() / 2.0, bound.get_Height() / 2.0);
		}
		private static double NormalizeAngle(double degree)
		{
			if (degree < 0.0 || degree > 360.0)
			{
				degree %= 360.0;
				if (degree < 0.0)
				{
					degree += 360.0;
				}
			}
			return degree;
		}
	}
}
